<html>
    <head>
        <style>
            body {
                width: 40em;
                margin-left: 25%;
            }
        </style>
            <link href="http://bitworking.org/css/b/base.css" type="text/css" rel="stylesheet" title="blocky" />
    </head>
    <body style="text-align:left">

    <h2>Gloves</h2>
<p>
Last time I talked about the creation of <a href="http://bitworking.org/news/Why_so_many_Python_web_frameworks">Robaccia</a>
I got to the point of a working framework and just waved
my hands and said you could keep going and "just" add conventions. 
I have pointed out that <a href="http://fishbowl.pastiche.org/2003/10/07/just_is_a_fourletter_word">"just" is a dangerous word</a>, 
so let's walk through the rest of the steps
to building a Rails/Django-like web framework.
</p>

<p>They key point of adding 'conventions'  is to take 
   a load off the user. You need to actually remove two kinds
   of load, cognitive and manual. Cognitive load is the number
   of concepts you need to hold in your head. The fewer the number of 
   concepts, and the more uniformly they are applied, the easier the
   system will be to use. Manual load is just the amount of 
   manual stuff, like typing, that you need to do. Why should
   I have to manually create a directory structure when a computer
   is capable of doing that? 
</p>

<h3>Motivation</h3>
<p class="aside">Before diving into lots of code and details
  let's clarify what I'm trying to accomplish here. I am <b>not</b>
  trying to write Rails for Python. My mission in creating this 
  software, and this write-up, is to lay out the core ideas 
  of constraints, cognitive load, and manual load, and how to apply 
  them to create a Rails/Django-like web framework, in the hopes 
  that it is helpful, so that <i>you</i> can 
  use it to create your own Rails-like framework in your language
  of choice.
</p>

<p class="aside">In this example I make some design decisions that
might seem a bit extreme. They are. You probably should make 
different choices for your web framework based on your programming
language and problem domain.
</p>

<h3>The Happy Path</h3>
<p>I am going to borrow a term from testing, <a href="http://en.wikipedia.org/wiki/Happy_path">Happy Path</a>,
   and use it in the context of creating our conventions. We need to pick
   a happy path when using our framework, and we need to knock down as many
   barriers, and make things as easy as possible, as long as users stick
   to that happy path.
</p>

<h3>Design</h3>

<p>If you are familiar with Ruby on Rails or Django then
the development story should be familiar. Here's the 
core of our design document. Creating an employee
application should consist of the following steps:</p>

<pre>
    $ robaccia createproject myproject 
    $ cd myproject 
    $ robaccia addmodelview employees <i># Creates  view, templates, and model.</i>
    $ gvim ./models/employees.py      <i># Add table columns.</i>
    $ robaccia createdb
    $ robaccia run
    $ firefox http://localhost:8080/employees/
</pre>

<p>
Of course, we may already have a model in place, and want
to just create another view on that model. Adding a view-only collection
should be as simple as:
</p>

<pre>
    $ robaccia addview employees
    $ robaccia run
    $ firefox http://localhost:8080/employees/
</pre>

<h3>Preliminaries</h3>

<p>
Currently Robaccia consists of a couple modules and a 
bunch of conventions for how to lay out a project.
They include the files: 
</p>

<ul>
<li><code>model.py</code> - One or more models expressed in SQLAlchemy Tables.</li>

<li><code>view.py</code> - One or more views, implemented as WSGI applications.</li>
<li><code>urls.py</code> - A single instance of a selector object that maps URIs to the WSGI applications in <code>view.py</code>.</li>
<li><code>templates</code> - A directory of Kid templates to be used to format the responses from the view applications.</li>
<li><code>dbconfig.py</code> - Configuration for the SQLAlchemy Tables in model.py</li>

</ul>

<p>
To accomodate making more complex projects we'll update most of those
files to be directories, and add another directory for log files:
</p>

<pre>
    /views
    /templates
    /models
    /logs
</pre>

<p>We'll leave <code>urls.py</code> as a file, but just rename it 
to <code>dispacher.py</code>, because while it does match
based on the incoming URI, it also matches on the request method,
so let's name it for what it does.</p>

<p>So let's look at what the directory structure we
 have for a Robaccia based application:
 </p>

<pre>
views/
models/
templates/
logs/
dispatcher.py
</pre>

 <p>And if we add a new resource to our application we distribute
 the model, view, and template files under each of those directories.
 </p>

<pre>
views/<b>name</b>.py
models/<b>name</b>.py
templates/<b>name</b>/list.html
templates/<b>name</b>/retrieve.html
...
logs/
</pre>

<p>
  Now Robaccia, using selector, allows you to dispatch to any
  view from any form of URI and any method. We need to simplify
  that, by adding constraints, and guide those constraints
  using a conceptual model. You can't possibly be surprised
  that I would choose a RESTful collection as an organizing principle.
  In particular, we'll assume that everything you want to create
  will fit into <a href="http://bitworking.org/news/wsgicollection">wsgicollection</a>.
  We will, obviously, build in an escape hatch, but RESTful
  collections will be our happy path.
</p>

<p>
  In general, using the notation of selector, we are looking at URIs 
  of the form:
  </p>

<pre><code> /...people/[{id}][;{noun}] </code></pre>
<p>
  And dispatching requests to URIs of that form to functions with nice names:
  </p>

<pre><code>  GET    /people               list()
  POST   /people               create()
  GET    /people/1             retrieve()
  PUT    /people/1             update()
  DELETE /people/1             delete()
  GET    /people;create_form   get_create_form()
  GET    /people/1;edit_form   get_edit_form()
  </code></pre>

<p>
We'll wrap all those target functions up into a single class and make
instances of those classes a WSGI application:
</p>

<pre><code><span class="PreProc">from</span> wsgicollection <span class="PreProc">import</span> Collection

<span class="Statement">class</span> <span class="Identifier">People</span>(Collection):

    <span class="Comment"># GET /people/</span>
    <span class="Statement">def</span> <span class="Identifier">list</span>(environ, start_response):
        <span class="Statement">pass</span>
    <span class="Comment"># POST /people/</span>
    <span class="Statement">def</span> <span class="Identifier">create</span>(environ, start_response):
        <span class="Statement">pass</span>
    <span class="Comment"># GET /people/1</span>
    <span class="Statement">def</span> <span class="Identifier">retrieve</span>(environ, start_response):
        <span class="Statement">pass</span>
    <span class="Comment"># PUT /people/1</span>
    <span class="Statement">def</span> <span class="Identifier">update</span>(environ, start_response):
        <span class="Statement">pass</span>
    <span class="Comment"># DELETE /people/1</span>
    <span class="Statement">def</span> <span class="Identifier">delete</span>(environ, start_response):
        <span class="Statement">pass</span>
    <span class="Comment"># GET /people/;create_form</span>
    <span class="Statement">def</span> <span class="Identifier">get_create_form</span>(environ, start_response):
        <span class="Statement">pass</span>
    <span class="Comment"># POST /people/1;comment_form</span>
    <span class="Statement">def</span> <span class="Identifier">post_comment_form</span>(environ, start_response):
        <span class="Statement">pass</span>
</code></pre>

<p>So if a collection is our organizing principle, what about our URI structure?
Let's keep it simple and presume everything will fall into a simple
URI template:
</p>
<pre><code>
/{view:alnum}/[{id:unreserved}][;{noun:unreserved}]
</code></pre>

<p>That is, each {view} represents a collection, and {view}/{id} is a member
of that collection. That's very simple and we have introduced a number of 
constraints by restricting ourselves to this URI structure:
</p>

<dl>
    <dt>Content type</dt>
    <dd>We presume that this collection has a single content type, such
       as HTML, or JSON. There is no provision for having a single collection
       serve up content in different media types. If you want to handle different
       media types then you can create two views that reference the same
       underlying model.
    </dd>

    <dt>Nesting</dt>
    <dd>The namespace is very flat, not allowing nesting of 
    resources beyond the simple collection. For example, if you were
    creating blogging application, you couldn't create 
    the main blog as a collection of entries, <code>/blog/{id}</code>,
    and then have a collection of comments for each blog entry, ala
    <code>/blog/{id}/comments/{commentid}</code>, but you could 
    have a collection that worked on all comments received, 
    <code>/comments/{id}</code>. Again, you can certainly
    do the nested collections for comments with the tools we are 
    going to supply, but that is not on the happy path. 
    </dd>
</dl>

<p>
  Also, in the intervening time since Robaccia was initially released
  I've written my own version of selector, 
  <a href="http://bitworking.org/news/163/WSGIDispatcher">wsgidispatcher</a>,
  so we'll switch to using that. The activity around Kid has diminished
  and moved mostly to Genshi, so we will also migrate to Genshi for templating.
</p>

<p>
    RESTful collections, and a highly constrained URI scheme, are the concepts
    we're using to reduce cognitive load.
    On the manual load side, we'll build some command line tools
    to automate the generation of stubs and directories.
</p>

<h3>Implementation</h3>
<p>
To fulfill our design we'll take the current Robaccia through a number
of incremental steps. The first step is modularization.
</p>

<h4>Modularize robaccia</h4>

<p>
We need to convert Robaccia from a set of conventions and 
a few lines of code into a library on it's own. First, we consolidate
all the code into a single installable module. We'll also create a stub
for the 'robaccia-admin' program, the one we'll eventually use to create 
the skeleton for project. 
</p>

<pre> 
   robaccia.py =&gt; robaccia/__init__.py # controversial in some quarters
   wsgidispatcher.py =&gt; robaccia/wsgidispatcher.py
   wsgicollection.py =&gt; robaccia/wsgicollection.py
   mimetypes.py =&gt; robaccia/mimetypes.py
</pre>

<p>
The only change that this requires in our code is a change in the imports
for wsgicollection and wsgidispatcher.
</p>

<p>
Now let's create a <code>setup.py</code> so our new module can be installed.
This uses the built-in <a href="http://docs.python.org/lib/module-distutils.html">distutils</a> library, and the configuration
  file is really a Python program.
</p>

<pre>
[<a href="http://robaccia.googlecode.com/svn/trunk/setup.py">setup.py</a>]
</pre>

<p>
Note that this setup file not only installs the robaccia library, but 
also the 'robaccia-admin' program.
</p>

<h4>robaccia-admin</h4>

<p>
We want to create a program that we can run that takes 
commands as arguments, just like 'svn' or 'bzr'. In addition we
want help, and also, like 'bzr', a way to make the
command set extensible. At the very least, we want to make
adding new command to 'robaccia-admin' easy. The simplest thing
that could possibly work is to have the command name 
match a method name in the module, and just look them up 
on the fly. 
</p>

<h3><tt>robaccia-bin</tt></h3>
<pre><code>members = globals()

<span class="Statement">if</span> __name__ == &quot;<span class="Constant">__main__</span>&quot;:
    <span class="Statement">try</span>:
        cmd = sys.argv[1]
    <span class="Statement">except</span>:
        cmd = &quot;<span class="Constant">help</span>&quot;
    args = sys.argv[2:]
    <span class="Statement">if</span> cmd <span class="Statement">not</span> <span class="Statement">in</span> members <span class="Statement">or</span>  
             cmd.startswith(&quot;<span class="Constant">_</span>&quot;) <span class="Statement">or</span> (<span class="Statement">not</span> callable(members[cmd])):
        cmd = &quot;<span class="Constant">help</span>&quot;
    
    members[cmd](args)
</code></pre>

<p>
Note that if a command isn't found in 'members', then
we assume the command "help". That gets us running commands, but
how do we do help? We need two kinds of help, a short message 
on how to use a command, used when we request a list of commands, and
a longer help description for when we want help on a single command.
Also, I don't want to write a separate help file.
</p>

<p>In Python if the first thing in your method is a string, then that
string is available via the __doc__ attribute. We'll put in place
a convention that the first line of the doc string is a short
description, and the whole doc string will be used for help
on that command. So, as an example, here is the implementation
for the 'commands' command. Run 'robaccia-admin commands' to get a list
of all the commands that the robaccia program supports. 
</p>

<h3><tt>robaccia-bin</tt></h3>
<pre><code><span class="Statement">def</span> <span class="Identifier">commands</span>(args):
    &quot;&quot;&quot;<span class="Constant">robaccia commands     </span>
<span class="Constant">    </span>
<span class="Constant">List all known commands that robaccia knows.</span>
&quot;&quot;&quot;
    <span class="Statement">for</span> name <span class="Statement">in</span> members:
        <span class="Statement">if</span> <span class="Statement">not</span> name.startswith(&quot;<span class="Constant">_</span>&quot;) <span class="Statement">and</span> callable(members[name]):
            <span class="Statement">print</span> members[name].__doc__.splitlines()[0]
</code></pre>

<p>
So now we can add the 'createproject' command. The 'createproject' command just
creates a directory structure populated with some default files. Looking back
into 'setup.py' you can see the 'package_data' parameter, which is a list
of non-source files that can be packaged with the library. 
We will stuff a skeleton project directory structure in there
and copy it over when we create a project.  We'll leverage Python's 
ability to introspect here, as each module has
a __file__ attribute which is the location of the source file, and 
we will use that to construct a path to the template files.
</p>

<h3><tt>robaccia-bin</tt></h3>
<pre><code><span class="Statement">def</span> <span class="Identifier">createproject</span>(args):
    <span class="Normal">&quot;&quot;&quot;</span><span class="Constant">robaccia createproject &lt;name&gt;   </span>

<span class="Constant">Creates a new project directory structure.</span>
<span class="Constant">The target directory must not exist.</span>
<span class="Constant">    </span><span class="Normal">&quot;&quot;&quot;</span>
    <span class="Statement">try</span>:
        name = args[0]
    <span class="Statement">except</span>:
        sys.exit(<span class="Normal">&quot;</span><span class="Constant">Error: Missing required parameter &lt;name&gt;.</span><span class="Normal">&quot;</span>)
    <span class="Statement">if</span> os.path.exists(name):
        sys.exit(<span class="Normal">&quot;</span><span class="Constant">Error: Directory '%s' already exists</span><span class="Normal">&quot;</span> % name)
    template_dir = os.path.abspath(
       os.path.join(robaccia.__file__, <span class="Normal">&quot;</span><span class="Constant">..</span><span class="Normal">&quot;</span>, <span class="Normal">&quot;</span><span class="Constant">templates</span><span class="Normal">&quot;</span>, <span class="Normal">&quot;</span><span class="Constant">project</span><span class="Normal">&quot;</span>)
    )
    shutil.copytree(template_dir, name)
</code></pre>

<h4>addview</h4>

<p>At this point let's skip to our second scenario and
  implement the 'addview' command. We'll go back later and 
  add the 'addmodelview' command.
</p>

<p>We'd like to keep the cognitive load as small as possible. 
If you look at the original implementation of Robaccia, each 
view had to render it's own template:</p>

<p><b>!! This is the old way, what were trying to replace  !!</b></p>
<pre><code>
<span class="PreProc">import</span> selector
<span class="PreProc">import</span> view

urls = selector.Selector()
urls.add(<span class="Normal">'</span><span class="Constant">/blog/</span><span class="Normal">'</span>, GET=view.list)
urls.add(<span class="Normal">'</span><span class="Constant">/blog/{id}/</span><span class="Normal">'</span>, GET=view.member_get)
</code></pre>

<p><b>!! This is the old way, what were trying to replace  !!</b></p>
<pre><code>
<span class="Statement">def</span> <span class="Identifier">list</span>(environ, start_response):
    rows = model.entry_table.select().execute()
    <span class="Statement">return</span> robaccia.render(start_response, <span class="Normal">'</span><span class="Constant">list.html</span><span class="Normal">'</span>, locals())
</code></pre>

<p>WSGICollection helps by adding an idiom and removing the need
to map each request URI and method to a WSGI application, but we 
can do better. We can take <code>robaccia.render()</code>
and presume that every renderer will conform to that signature. 
Since we have a convention for how files will be laid out, we can
do even more work, we can look up the template to render using
the name of the WSGICollection that was called, and the name of the 
member function that was called.
The only piece of information we are missing is the file extension
of the template, so we will have to pass that in also.
</p>
<p>What we want, for example, is that if we add a collection 'fred':
<pre>
   /views/<b>fred</b>.py
   /templates/<b>fred</b>/list.html
   /templates/<b>fred</b>/retreive.html
</pre>
<p>
Then a GET to <tt>/fred/</tt> will 
end up calling <code>views.fred.app.list()</code>, and the template
<tt>/templates/<b>fred</b>/list.html</tt> will be rendered.
</p>

<p>That's fine default behavior, but we need a way to signal whether we want
the default rendering to occur, of the collection view member function
has decided to over-ride the default behavior and do it's own processing.
</p>

<p>In normal processing a wsgicollection member function
returns an iterable. We can look for things besides 
iterables to indicate that we should look for a template and render it.
The simplest thing is to look for a dictionary, since that is what you
pass into a template to get rendered. We can also accept 'None'
in the response and convert that into some sort of acceptable dictionary to be
passed into the templating engine.
</p>

<p>Here is the implementation for DefaultCollection, which implements the above design:
</p>

<h3><tt>defaultcollection.py</tt></h3>
<pre><code><span class="PreProc">from</span> wsgicollection <span class="PreProc">import</span> Collection
<span class="PreProc">import</span> os

<span class="Statement">class</span> <span class="Identifier">DefaultCollection</span>(Collection):

    <span class="Statement">def</span> <span class="Identifier">__init__</span>(self, ext, renderer):
        Collection.__init__(self)
        self._ext = ext
        self._renderer = renderer

    <span class="Statement">def</span> <span class="Identifier">__call__</span>(self, environ, start_response):
        response = Collection.__call__(self, environ, start_response)
        <span class="Statement">if</span> response == None:
            <span class="Statement">if</span> self._id:
                response = {<span class="Normal">'</span><span class="Constant">id</span><span class="Normal">'</span>: self._id}
            <span class="Statement">else</span>:
                response = {}
        <span class="Statement">if</span> isinstance(response, dict):
            view = environ[<span class="Normal">'</span><span class="Constant">wsgiorg.routing_args</span><span class="Normal">'</span>][1].get(<span class="Normal">'</span><span class="Constant">view</span><span class="Normal">'</span>, <span class="Normal">'</span><span class="Constant">.</span><span class="Normal">'</span>)
            template_file = os.path.join(view, self._function_name + <span class="Normal">&quot;</span><span class="Constant">.</span><span class="Normal">&quot;</span> + self._ext)

            <span class="Statement">return</span> self._renderer(environ, start_response, template_file, response)
        <span class="Statement">else</span>:
            <span class="Statement">return</span> response
</code></pre>

<p>Note that we hand off most of the work to wsgicollection.Collection, and
then if the response is a dictionary we look up the template 
and pass the name into the renderer. The only other thing to note is that
the construtor now takes two new  arguments, the template filename extension,
and the renderer.
</p>

<p>DefaultCollection now vastly simplifies our views if we 
   just want the default templates rendered:
</p>

<pre>
<span class="PreProc">from</span> robaccia.defaultcollection <span class="PreProc">import</span> DefaultCollection
<span class="PreProc">from</span> robaccia <span class="PreProc">import</span> render


<span class="Statement">class</span> <span class="Identifier">Collection</span>(DefaultCollection):

    <span class="Comment"># GET /{view}/</span>
    <span class="Statement">def</span> <span class="Identifier">list</span>(self, environ, start_response):
        <span class="Statement">pass</span>

    <span class="Comment"># GET /{view}/{id}</span>
    <span class="Statement">def</span> <span class="Identifier">retrieve</span>(self, environ, start_response):
        <span class="Statement">pass</span>

    <span class="Comment"># PUT /{view}/{id}</span>
    <span class="Statement">def</span> <span class="Identifier">update</span>(self, environ, start_response):
        <span class="Statement">pass</span>

    <span class="Comment"># DELETE /{view}/{id}</span>
    <span class="Statement">def</span> <span class="Identifier">delete</span>(self, environ, start_response):
        <span class="Statement">pass</span>

    <span class="Comment"># POST /{view}/</span>
    <span class="Statement">def</span> <span class="Identifier">create</span>(self, environ, start_response):
        <span class="Statement">pass</span>

app = Collection(<span class="Normal">'</span><span class="Constant">html</span><span class="Normal">'</span>, render)
</pre>

<p>To stop handling a particular URI, such as deleting a member of the collection, 
just remove the associated member function. 
</p>

<h4>run</h4>

<p>We are almost done with our first scenario. All we need now is a 
way to run our application. Since we based Robaccia on WSGI we can use
<a href="http://docs.python.org/lib/module-wsgiref.html">wsgiref</a> to run our application under a local web server for development
purposes.
</p>

<h3><tt>robaccia-admin</tt></h3>
<pre><code><span class="Statement">def</span> <span class="Identifier">run</span>(args):
    <span class="Normal">&quot;&quot;&quot;</span><span class="Constant">robaccia run           </span>

<span class="Constant">Start running the application under</span>
<span class="Constant">a local web server.</span>
<span class="Normal">&quot;&quot;&quot;</span>
    <span class="PreProc">from</span> dispatcher <span class="PreProc">import</span> app
    <span class="PreProc">from</span> wsgiref.simple_server <span class="PreProc">import</span> WSGIServer, WSGIRequestHandler
    robaccia.init_logging()
    httpd = WSGIServer((<span class="Normal">''</span>, 3100), WSGIRequestHandler)
    httpd.set_app(app)
    <span class="Statement">print</span> <span class="Normal">&quot;</span><span class="Constant">Serving HTTP on %s port %s ...</span><span class="Normal">&quot;</span> % httpd.socket.getsockname()
    httpd.serve_forever()
</code></pre>

<p>Note again we know the WSGI app to load because of our file layout conventions.
The last piece of the puzzle is how dispatcher.py routes the incoming
requests to the right view.
</p>

<h3><tt>dispatcher.py</tt></h3>
<pre><code><span class="PreProc">from</span> robaccia.wsgidispatcher <span class="PreProc">import</span> Dispatcher
<span class="PreProc">from</span> robaccia <span class="PreProc">import</span> deferred_collection

app = Dispatcher()
app.add(<span class="Normal">'</span><span class="Constant">/{view:alnum}/[{id:unreserved}][;{noun:unreserved}]</span><span class="Normal">'</span>, deferred_collection)

</code></pre>

<p>A <tt>dispatcher.py</tt> is copied into every project so that you can customize it later
  if you want to do something besides the default everything-is-a-collection convention.
  Any URI templates added before the one that's already there will match first.
</p>

<p>It's <tt>deferred_collection</tt> that does our lookup of the view to call.</p>

<h3><tt>deferred_collection()</tt></h3>
<pre><code><span class="Statement">def</span> <span class="Identifier">deferred_collection</span>(environ, start_response):
    <span class="Normal">&quot;&quot;&quot;</span><span class="Constant">Look for a views.* module to handle this incoming</span>
<span class="Constant">    request. Presumes the module has </span>
<span class="Constant">    an 'app' that is a WSGI application.</span><span class="Normal">&quot;&quot;&quot;</span>
    <span class="Comment"># Pull out the view name from the template parameters</span>
    view = environ[<span class="Normal">'</span><span class="Constant">wsgiorg.routing_args</span><span class="Normal">'</span>][1][<span class="Normal">'</span><span class="Constant">view</span><span class="Normal">'</span>]
    <span class="Comment"># Load the named view from the 'views' directory</span>
    m = __import__(<span class="Normal">&quot;</span><span class="Constant">views.</span><span class="Normal">&quot;</span> + view, globals(), locals())
    <span class="Comment"># Pass along the WSGI call into the given application</span>
    logging.getLogger(<span class="Normal">'</span><span class="Constant">robaccia</span><span class="Normal">'</span>).debug(<span class="Normal">&quot;</span><span class="Constant">View: %s</span><span class="Normal">&quot;</span> % view)
    <span class="Statement">return</span> getattr(getattr(m, view), <span class="Normal">'</span><span class="Constant">app</span><span class="Normal">'</span>)(environ, start_response)
</code></pre>

<p>We construct the view name from the incoming path and then load
that module dynamically. Of course a little error handling, like returning 
a 404 if the module isn't found, would be good, but you get the idea.
</p>

<p>So that finishes it, our first phase is complete. If we include a couple
simple templates for 'list.html' and 'retrieve.html' then we can run
the second scenario with this code:</p>

<pre>
joe@joe-laptop:~$ <b>robaccia-admin createproject myproject</b>
joe@joe-laptop:~$ <b>cd myproject/</b>
<i>/home/joe/myproject</i>
joe@joe-laptop:~/myproject$ <b>robaccia-admin addview fred</b>
<i> created views/fred.py 
 created templates/fred/list.html 
 created templates/fred/retrieve.html </i>
joe@joe-laptop:~/myproject$ <b>robaccia-admin run</b>
<i>Serving HTTP on 0.0.0.0 port 3100 ...
localhost - - [24/May/2007 11:14:49] "GET /fred/ HTTP/1.1" 200 116</i>
</pre>

<p>Pointing our browser at <tt>http://localhost:3100/fred/</tt> gets us:</p>

<p><img src="http://bitworking.org/images/2007/05/robaccia-fred-list.png" alt="Web page: Hello World!"/></p>

<h3>Phase Two - Model</h3>

<p>Now let's get cracking on adding in the database.</p>

<p class="aside">If you know me, you know I'm not a huge fan of
  relational databases, I prefer my stores more scalable and 
  my columns sparse, but let's not talk about that now. 
  All of the next generation web frameworks rely on a relational 
  database and this section will show how to do just that.  
</p>

<p>The very first thing we are going to need is a 
  configuration file for the database, which we'll use
  <tt>dbconfig.py</tt> and throw that into the default project
  files so it is always present.
</p>

<p>On top of the files we added for 'addview'
we will add in a model, and some template code for adding and editing
collection members.
</p>

<p>DefaultModelCollection 
does everything that DefaultCollection does, but now we have a 'model'
to keep track of. Those dictionaries are going from the model
into templates. There is also a way to parse incoming request bodies.
</p>
  
<h3><tt>defaultmodelcollection.py</tt></h3>
<pre><code><span class="PreProc">from</span> wsgicollection <span class="PreProc">import</span> Collection
<span class="PreProc">import</span> os
<span class="PreProc">from</span> robaccia <span class="PreProc">import</span> http200, http405, http404, http303

<span class="Statement">class</span> <span class="Identifier">DefaultModelCollection</span>(Collection):

    <span class="Statement">def</span> <span class="Identifier">__init__</span>(self, ext, renderer, parser, model):
        Collection.__init__(self)
        self._ext = ext
        self._renderer = renderer <span class="Comment"># converts dicts to representations</span>
        self._model = model
        self._parser = parser     <span class="Comment"># converts representations to dicts</span>
        self._repr = {}           <span class="Comment"># request representation as a dict()</span>

    <span class="Statement">def</span> <span class="Identifier">__call__</span>(self, environ, start_response):
        response = Collection.__call__(self, environ, start_response)
        <span class="Statement">if</span> environ[<span class="Normal">'</span><span class="Constant">REQUEST_METHOD</span><span class="Normal">'</span>] <span class="Statement">in</span> [<span class="Normal">'</span><span class="Constant">PUT</span><span class="Normal">'</span>, <span class="Normal">'</span><span class="Constant">POST</span><span class="Normal">'</span>]:
            self._repr = self._parser(environ)
        <span class="Statement">if</span> response == None:
            primary = self._model.primary_key.columns.keys()[0]
            view = environ[<span class="Normal">'</span><span class="Constant">wsgiorg.routing_args</span><span class="Normal">'</span>][1].get(<span class="Normal">'</span><span class="Constant">view</span><span class="Normal">'</span>, <span class="Normal">'</span><span class="Constant">.</span><span class="Normal">'</span>)
            template_file = os.path.join(view, self._function_name + <span class="Normal">&quot;</span><span class="Constant">.</span><span class="Normal">&quot;</span> + self._ext)
            method = environ.get(<span class="Normal">'</span><span class="Constant">REQUEST_METHOD</span><span class="Normal">'</span>, <span class="Normal">'</span><span class="Constant">GET</span><span class="Normal">'</span>)
            <span class="Statement">if</span> self._id:
                <span class="Statement">if</span> method == <span class="Normal">&quot;</span><span class="Constant">POST</span><span class="Normal">&quot;</span> <span class="Statement">and</span> <span class="Normal">&quot;</span><span class="Constant">_method</span><span class="Normal">&quot;</span> <span class="Statement">in</span> 
                    self._repr <span class="Statement">and</span> self._repr[<span class="Normal">&quot;</span><span class="Constant">_method</span><span class="Normal">&quot;</span>] <span class="Statement">in</span> [<span class="Normal">&quot;</span><span class="Constant">PUT</span><span class="Normal">&quot;</span>, <span class="Normal">&quot;</span><span class="Constant">DELETE</span><span class="Normal">&quot;</span>]:
                    method = self._repr[<span class="Normal">&quot;</span><span class="Constant">_method</span><span class="Normal">&quot;</span>]
                    <span class="Statement">del</span> self._repr[<span class="Normal">&quot;</span><span class="Constant">_method</span><span class="Normal">&quot;</span>]
                <span class="Statement">if</span> method == <span class="Normal">'</span><span class="Constant">GET</span><span class="Normal">'</span>:
                    result = self._model.select(self._model.c[primary]==self._id
                       ).execute()
                    row = result.fetchone()
                    <span class="Statement">if</span> None == row:
                        <span class="Statement">return</span> http404(environ, start_response)
                    data = dict(zip(result.keys, row))
                    <span class="Statement">return</span> self._renderer(environ, start_response, template_file, 
                          {<span class="Normal">&quot;</span><span class="Constant">row</span><span class="Normal">&quot;</span>: data, <span class="Normal">&quot;</span><span class="Constant">primary</span><span class="Normal">&quot;</span>: primary})
                <span class="Statement">elif</span> method == <span class="Normal">'</span><span class="Constant">PUT</span><span class="Normal">'</span>:
                    self._model.update(self._model.c[primary]==self._id
                        ).execute(self._repr)
                    <span class="Statement">return</span> http303(environ, start_response, self._id)
                <span class="Statement">elif</span> method == <span class="Normal">'</span><span class="Constant">DELETE</span><span class="Normal">'</span>:
                    self._model.delete(self._model.c[primary]==self._id).execute()
                    <span class="Statement">return</span> http303(environ, start_response, <span class="Normal">&quot;</span><span class="Constant">./</span><span class="Normal">&quot;</span>)
                <span class="Statement">else</span>:
                    <span class="Statement">print</span> method
                    <span class="Statement">return</span> http405(environ, start_response)
            <span class="Statement">else</span>:
                <span class="Statement">if</span> method == <span class="Normal">'</span><span class="Constant">GET</span><span class="Normal">'</span>:
                    result = self._model.select().execute()
                    meta = self._model.columns.keys()
                    data = [dict(zip(result.keys, row)) <span class="Statement">for</span> row <span class="Statement">in</span> result.fetchall()]
                    <span class="Statement">return</span> self._renderer(environ, start_response, template_file, 
                       {<span class="Normal">&quot;</span><span class="Constant">data</span><span class="Normal">&quot;</span>: data, <span class="Normal">&quot;</span><span class="Constant">primary</span><span class="Normal">&quot;</span>: primary, <span class="Normal">&quot;</span><span class="Constant">meta</span><span class="Normal">&quot;</span>: meta})
                <span class="Statement">elif</span> method == <span class="Normal">'</span><span class="Constant">POST</span><span class="Normal">'</span>:
                    self._model.insert(self._repr).execute()
                    <span class="Statement">return</span> http303(environ, start_response, <span class="Normal">&quot;</span><span class="Constant">.</span><span class="Normal">&quot;</span>)
        <span class="Statement">else</span>:
            <span class="Statement">return</span> response

</code></pre>

<p>Note that the constructor takes a renderer, a parser, and a model.
   The renderer takes dictionaries and turns them into response bodies.
   The parser takes incoming request bodies and turns them 
   into dictionaries. We already have an HTML renderer, all we need to do
   form processing is something that takes incoming 'application/x-www-form-urlencoded'
   data and converts it into a dictionary, which is easy to come by.
</p>

<p>We need to update our 'view' to handle the 'edit' and 'new' forms, which
just means adding 'get_edit_form()' and 'get_new_form()' functions to the 
view. The templates are also pretty simple. Here is the updated
'list.html':</p>

<h3><tt>list.html</tt></h3>
<pre><code><span class="Identifier">&lt;</span><span class="Statement">html</span><span class="Identifier"> xmlns=</span><span class="Constant">&quot;<a href="http://www.w3.org/1999/xhtml">http://www.w3.org/1999/xhtml</a>&quot;</span><span class="Identifier"> xmlns:py=</span><span class="Constant">&quot;<a href="http://genshi.edgewall.org/">http://genshi.edgewall.org/</a>&quot;</span><span class="Identifier">  &gt;</span>
    <span class="Identifier">&lt;</span><span class="Statement">head</span><span class="Identifier">&gt;</span>
<span class="PreProc">        </span><span class="Identifier">&lt;</span><span class="Statement">title</span><span class="Identifier">&gt;</span><span class="Title">Fred</span><span class="Identifier">&lt;/</span><span class="Statement">title</span><span class="Identifier">&gt;</span><span class="PreProc"> </span>
<span class="PreProc">    </span><span class="Identifier">&lt;/</span><span class="Statement">head</span><span class="Identifier">&gt;</span>

    <span class="Identifier">&lt;</span><span class="Statement">body</span><span class="Identifier">&gt;</span>
        <span class="Identifier">&lt;</span><span class="Statement">p</span><span class="Identifier">&gt;&lt;</span><span class="Statement">a</span><span class="Identifier"> </span><span class="Type">href</span><span class="Identifier">=</span><span class="Constant">&quot;;new_form&quot;</span><span class="Identifier">&gt;</span><span class="Underlined">New</span><span class="Identifier">&lt;/</span><span class="Statement">a</span><span class="Identifier">&gt;&lt;/</span><span class="Statement">p</span><span class="Identifier">&gt;</span>
        <span class="Identifier">&lt;</span><span class="Statement">p</span><span class="Identifier"> py:</span><span class="Type">for</span><span class="Identifier">=</span><span class="Constant">&quot;row in data&quot;</span><span class="Identifier">&gt;</span>
            <span class="Identifier">&lt;</span><span class="Statement">p</span><span class="Identifier"> py:</span><span class="Type">for</span><span class="Identifier">=</span><span class="Constant">&quot;key, value in row.iteritems()&quot;</span><span class="Identifier">&gt;</span>
                <span class="Identifier">&lt;</span><span class="Statement">b</span><span class="Identifier">&gt;</span><span class="htmlBold">$key</span><span class="Identifier">&lt;/</span><span class="Statement">b</span><span class="Identifier">&gt;</span>: $value
            <span class="Identifier">&lt;/</span><span class="Statement">p</span><span class="Identifier">&gt;</span>
            <span class="Identifier">&lt;</span><span class="Statement">a</span><span class="Identifier"> </span><span class="Type">href</span><span class="Identifier">=</span><span class="Constant">&quot;${row[primary]}&quot;</span><span class="Identifier">&gt;</span><span class="Underlined">${row[primary]}</span><span class="Identifier">&lt;/</span><span class="Statement">a</span><span class="Identifier">&gt;</span>
            <span class="Identifier">&lt;</span><span class="Statement">hr</span><span class="Identifier">/&gt;</span>
        <span class="Identifier">&lt;/</span><span class="Statement">p</span><span class="Identifier">&gt;</span>
    <span class="Identifier">&lt;/</span><span class="Statement">body</span><span class="Identifier">&gt;</span>

<span class="Identifier">&lt;/</span><span class="Statement">html</span><span class="Identifier">&gt;</span>
</code></pre>



<p>And now we're able to meet our initial design:</p>

<pre>
joe@joe-laptop:~$ <b>robaccia-admin createproject myprj</b>
joe@joe-laptop:~$ <b>cd myprj/</b>
<i>/home/joe/myprj</i>
joe@joe-laptop:~/myprj$ <b>robaccia-admin addmodelview employees</b>
<i> created views/employees.py 
 created models/employees.py 
 created templates/employees/list.html 
 created templates/employees/retrieve.html 
 created templates/employees/get_edit_form.html 
 created templates/employees/get_new_form.html </i>
joe@joe-laptop:~/myprj$ <b>vim models/employees.py</b>
joe@joe-laptop:~/myprj$ <b>cat models/employees.py</b>
<i>from sqlalchemy import Table, Column, Integer, String
import dbconfig

table = Table('employees', dbconfig.metadata,
        Column('id', Integer(), primary_key=True),
        Column('name', String(250)), 
        Column('title', String(250)),
        Column('office_number', Integer()),
        )
</i>
joe@joe-laptop:~/myprj$ <b>robaccia-admin createdb</b>
joe@joe-laptop:~/myprj$ <b>robaccia-admin run</b>
<i>Serving HTTP on 0.0.0.0 port 3100 ...</i>
</pre>


<p>The database is initially empty.  Click on New to create a new member in the collection.  </p>
<p><img src="http://bitworking.org/images/2007/05/robaccia-employees-new.png" alt="Web page: New"/></p>
<p>Creating a new employee.</p>
<p><img src="http://bitworking.org/images/2007/05/robaccia-employees-new-form.png" alt="Web page: Form for creating a new employee"/></p>
<p>Our collection list afer adding couple new employees. Note the link to individual employees.</p>
<p><img src="http://bitworking.org/images/2007/05/robaccia-employees-list.png" alt="Web page: List of two added employees"/></p>
<p>From an individual collection member we can edit and delete the employee.</p>
<p><img src="http://bitworking.org/images/2007/05/robaccia-employees-retrieve.png" alt="Web page: A single employee"/></p>
<p>A form for editing the employee.</p>
<p><img src="http://bitworking.org/images/2007/05/robaccia-employees-edit-form.png" alt="Web page: Editing a single employee"/></p>

<h3>Lessons</h3>

<p>We came up with a rather simple database to web framework, but in reality
 haven't lost any flexibility over the original Robaccia. All the pieces 
 of URI dispatching, views and templates are present, all we did
 was pave the happy path.
</p>

<p>We paved the happy path by embracing the following constraints:</p>
<ol>
    <li>All resource fit into a collection.</li>
    <li>A single URI path structure.</li>
    <li>A fixed model interface: SQLAlchemy.</li>
    <li>A fixed project layout.</li>
    <li>WSGI as method of communication between components.</li>
    <li>A single media-type for any single view. (You can
        have different views with different media-types that
        use the same model.) 
    </li>
    <li>Not really mentioned explicitly, but all the configuration
        was done through Python files.</li>
</ol>

<h3>Observations</h3>

<p>The code so far has renderers and parsers for HTML and
HTML forms, but we could easily add support for JSON or Atom.
</p>

<p>There isn't much that ties us to SQLAlchemy; just DefaultModelDispatcher
and the initial files put down by 'robaccia addmodelview'. We could easily 
switch out to another ORM, or even drop the RDBMS and move to another
kind of data store.
</p>

<p>There is a whole slew of stuff that hasn't been done like 
   handling changes to the database, deployment, security,  
   large collections that require paging, etc. Those are all solvable
   problems, and not the point of this exercise, which was to 
   demonstrate applying constraints to reduce cognitive and manual 
  load to create a Rails/Django-like web framework. 
</p>

<p>The code is now available on <a href="http://code.google.com/p/robaccia/">code.google.com</a>.
If you run it under Python 2.5 you will only need to install SQLAlchemy and Genshi. 
For Python 2.4 you will also need to install wsgiref.
</p>

</body>
